﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace TfsBugger.Core
{
    public abstract class Bugger : INotifyPropertyChanged, IEquatable<Bugger>, IComparable<Bugger>, IComparable
    {
        public Bugger() 
        {
            m_name = GetDefaultName();
        }


        public event EventHandler ArgsLoaded;
        public event EventHandler BeginWork;
        public event EventHandler<TargetUpdatedEventArgs> TargetUpdated;
        public event EventHandler<PollingStatusUpdatedEventArgs> PollingStatusUpdated;
        public event EventHandler<BuggerWorkFinishedEventArgs> WorkFinished;


        #region INotifyPropertyChanged Members & Helpers
        public event PropertyChangedEventHandler PropertyChanged;


        protected void RaisePropertyChanged(PropertyChangedEventArgs e)
        {
            if (null != PropertyChanged)
                PropertyChanged(this, e);
        }
        #endregion 


        #region Statics
        private static Dictionary<Type, int> m_autoNameIndex = new Dictionary<Type, int>();


        private static Dictionary<Type, int> AutoNameIndex
        {
            get { return m_autoNameIndex; }
        }
        #endregion


        #region Fields
        private ManualResetEvent m_workingHandle = new ManualResetEvent(false);
        private AutoResetEvent m_userCancelHandle = new AutoResetEvent(false);
        private AsyncOperation m_asyncOp;
        private int m_waitInterval = 5;

        private Guid m_id = Guid.NewGuid();
        private string m_name;
        private BuggerArgs m_args;
        #endregion 


        #region Properties 

        protected AsyncOperation AsyncOp
        {
            get { return m_asyncOp; }
        }


        public int WaitInterval
        {
            get { return m_waitInterval; }
            set { m_waitInterval = value; }
        }

        public Guid Id
        {
            get { return m_id; }
        }


        public string Name
        {
            get { return m_name; }
            set
            {
                if (m_name == value)
                    return;

                m_name = value;
                RaisePropertyChanged(new PropertyChangedEventArgs("Name"));
            }
        }


        public BuggerArgs Args
        {
            get { return m_args; }
        }


        public bool HasArgs
        {
            get { return null != m_args; }
        }


        public bool IsBusy
        {
            get { return m_workingHandle.WaitOne(0, false); }
        }


        public ManualResetEvent WorkingHandle
        {
            get { return m_workingHandle; }
        }


        public bool CancellationPending
        {
            get { return m_userCancelHandle.WaitOne(0, false); }
        }
        #endregion


        #region Event Raising & Marshaling Helpers 
        protected void RaiseArgsLoaded()
        {
            if (null != ArgsLoaded)
                ArgsLoaded(this, EventArgs.Empty);
        }


        protected void RaiseBeginWork()
        {
            m_asyncOp.Post(state =>
                {
                    if (null != this.BeginWork)
                        BeginWork(this, EventArgs.Empty);
                },
                null);
        }


        protected void RaiseTargetUpdated(TargetUpdatedEventArgs args)
        {
            m_asyncOp.Post(state =>
                {
                    if (null != this.TargetUpdated)
                        TargetUpdated(this, (TargetUpdatedEventArgs)state);
                },
                args);
        }


        protected void RaisePollingStatusUpdated(PollingStatusUpdatedEventArgs args)
        {
            m_asyncOp.Post(state =>
                {
                    if (null != this.PollingStatusUpdated)
                        PollingStatusUpdated(this, (PollingStatusUpdatedEventArgs)state);
                },
                args);
        }


        protected void RaiseWorkFinished(BuggerWorkFinishedEventArgs args)
        {
            m_asyncOp.PostOperationCompleted(state =>
                {
                    if (null != this.WorkFinished)
                        WorkFinished(this, (BuggerWorkFinishedEventArgs)state);
                },
                args);
        } 
        #endregion


        #region Public Methods 
        public void SetArgs(BuggerArgs args)
        {
            m_args = args;
            RaiseArgsLoaded();
        }


        public void RunAsync()
        {
            if (m_workingHandle.WaitOne(0, false))
                throw new InvalidOperationException("Only one asynchronous operation can be run at a time for any bugger instance.");

            if (!CheckArgsLoaded())
                throw new InvalidOperationException("A bugger must have start argument before running.");

            m_workingHandle.Set();

            //Using this will automatically do thread affinity checking & marshaling 
            //for our Raise-XXX methods 
            //if the bugger is being invoked by a UI component 
            //or anything that runs in a single-threaded context, so client 
            //event handlers can do work safely
            m_asyncOp = AsyncOperationManager.CreateOperation(null);

            //ThreadStart is used here, but can be any delegate - all that is needed 
            //here is a delegate signature that returns void and takes no arguments
            ThreadStart work = () => DoWork(m_args);
            work.BeginInvoke(OnAsyncWorkCompleted, null);

            RaiseBeginWork();
        }


        public void CancelAsync()
        {
            if (!m_workingHandle.WaitOne(0, false))
                return;

            PollingStatusUpdatedEventArgs pargs = new PollingStatusUpdatedEventArgs();
            pargs.Status = "Cancelling...";
            RaisePollingStatusUpdated(pargs);
            m_userCancelHandle.Set();
        } 
        #endregion


        #region Work/Helper Methods 
        private void OnAsyncWorkCompleted(IAsyncResult result)
        {
            BuggerWorkFinishedEventArgs args = new BuggerWorkFinishedEventArgs();
            try
            {
                AsyncResult r = (AsyncResult)result;
                ((ThreadStart)r.AsyncDelegate).EndInvoke(result);
            }
            catch
            {
                throw;
            }
            finally
            {
                m_workingHandle.Reset();
                m_userCancelHandle.Reset();
                RaisePollingStatusUpdated(new PollingStatusUpdatedEventArgs { Status = "Ready" });
            }

            RaiseWorkFinished(args);
        }


        protected virtual void DoWork(object args)
        {
            if (null == Args.ServerManager || !Args.ServerManager.Server.HasAuthenticated)
                throw new ArgumentException("Team Foundation Server instance was either null or cannot establish a connection.");

            Init();
            PollingStatusUpdatedEventArgs pollArgs = new PollingStatusUpdatedEventArgs();
            while (true)
            {
                if (CancellationPending)
                    break;

                pollArgs.Status = "Resting";
                RaisePollingStatusUpdated(pollArgs);
                Thread.Sleep(WaitInterval * 1000);

                pollArgs.Status = "Bugging server for updates...";
                RaisePollingStatusUpdated(pollArgs);
                Args.ServerManager.Server.EnsureAuthenticated();
                try
                {
                    Poll();
                }
                catch (Exception ex)
                {
                    pollArgs.Status = "Refreshing server connection...";
                    RaisePollingStatusUpdated(pollArgs);

                    Args.ServerManager.Server.Dispose();
                    Args.ServerManager.Load();
                    Poll();
                }
            }
        }


        protected string GetDefaultName()
        {
            Type myType = GetType();
            if (!AutoNameIndex.ContainsKey(myType))
                AutoNameIndex.Add(myType, 0);

            AutoNameIndex[myType]++;
            return string.Format("{0}{1}", myType.Name, AutoNameIndex[myType]);
        }
        #endregion


        #region Abstract Methods
        public abstract bool CheckArgsLoaded();

        protected abstract void Init();

        protected abstract void Poll(); 
        #endregion


        #region IEquatable<Bugger> Members
        public bool Equals(Bugger other)
        {
            return (null != other && Id == other.Id);
        }
        #endregion


        #region IComparable<Bugger> Members
        public int CompareTo(Bugger other)
        {
            return Id.CompareTo(other.Id);
        }
        #endregion


        #region IComparable Members
        public int CompareTo(object obj)
        {
            if (!(obj is Bugger))
                return -1;

            return CompareTo((Bugger)obj);
        }
        #endregion
    }      


    public class BuggerWorkFinishedEventArgs : EventArgs
    {

        
    }
}
